﻿namespace EFS.RC.CCC.Channels.Customers
{
	/// <summary>
	/// supervisor context channel
	/// </summary>
	internal class OfficeService : EFS.RC.Common.AsyncRoot
	{
		// sync access
		private object syncAccess = new object();

		// office conference
		private EFS.RC.CCC.Confereces.Customers.OfficeService officeServiceCustomerConference = null;
		
		// call 
		private Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall serviceCall;
		
		/// <summary>
		/// Constructor
		/// </summary>
		/// <param name="conversation"></param>
		/// <param name="remoteEndpoint"></param>
		public OfficeService(EFS.RC.CCC.Confereces.Customers.OfficeService officeServiceCustomerConference)
		{
			// check
			EFS.Common.Verify.Reference(officeServiceCustomerConference, "officeServiceCustomerConference");

			// make new
			this.officeServiceCustomerConference = officeServiceCustomerConference;

			// log
			EFS.EventLog.Log.LogInfo("SupervisorContext", this);
		}

		/// <summary>
		/// Describe this object into a nice formated string
		/// </summary>
		/// <returns></returns>
		public override System.String Describe()
		{
			return System.String.Format("EFS.RC.CCC.Channels.Customers.OfficeService");
		}

		/// <summary>
		/// Begin platform startup
		/// </summary>
		public EFS.RC.Common.AsyncProcess BeginStartup()
		{
		    // log
		    EFS.EventLog.Log.LogInfo("BeginStartup", this);

		    // start end point
		    return BeginAsyncProcess(Startup, OnStartupComplete);
		}

		/// <summary>
		/// Begin platform startup
		/// </summary>
		public EFS.RC.Common.AsyncProcess BeginShutdown()
		{
		    // log
		    EFS.EventLog.Log.LogInfo("BeginStartup", this);

		    // start end point
		    return BeginAsyncProcess(Shutdown, OnShutdownComplete);
		}

		#region Startup

		/// <summary>
		/// Startup
		/// </summary>
		private void Startup()
		{
			// make service call 
			serviceCall = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCall(officeServiceCustomerConference.Conversation);
            serviceCall.StateChanged += CallStateChanged;
            serviceCall.AudioVideoFlowConfigurationRequested += new 
				System.EventHandler<Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoFlowConfigurationRequestedEventArgs>(AudioVideoFlowConfigurationRequested);
			
			// options
            Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCallEstablishOptions options = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoCallEstablishOptions();
            options.UseGeneratedIdentityForTrustedConference = false;
            options.AudioVideoMcuDialInOptions.RemoveFromDefaultRouting = true;

            try
            {
				// establish
				serviceCall.BeginEstablish(options, EndEstablisServiceCall, null);
            }
            catch (System.InvalidOperationException ivoex)
            {
				EFS.EventLog.Log.LogException(ivoex, this);

				// shut it down
				BeginShutdown();
            }
	
			// join office conferenec
			try
			{
				// TODO: REMOVE this
				// join conference
				//officeServiceCustomerConference.Conversation.ConferenceSession.BeginJoin(officeServiceCustomerConference.Conversation.LocalParticipant.Uri, null, EndJoinOfficeConference, null);
			}
			catch (System.InvalidOperationException ivoex)
			{
				// log
				EFS.EventLog.Log.LogException(ivoex, this);

				// shut down
				BeginShutdown();
			}
		}

		/// <summary>
		/// End establish
		/// </summary>
		/// <param name="result"></param>
		public void EndJoinOfficeConference(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndEstablisContextChannel", this);

			try
			{
				// try 
				officeServiceCustomerConference.Conversation.ConferenceSession.EndJoin(ar);


			}
			catch (Microsoft.Rtc.Signaling.RealTimeException rtex)
			{
				EFS.EventLog.Log.LogException(rtex, this);

				// shut it down
				BeginShutdown();
			}
		}

		/// <summary>
		/// End establish
		/// </summary>
		/// <param name="result"></param>
		public void EndEstablisServiceCall(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndEstablisContextChannel", this);

			try
			{
				// try 
				serviceCall.EndEstablish(ar);

				// make private audio channel
				// EstablishPrivateAudioChannel();

				// make b2b call
				// officeServiceCustomerConference.CustomerSession.MakeB2BCall();
			}
			catch (Microsoft.Rtc.Signaling.RealTimeException rtex)
			{
				EFS.EventLog.Log.LogException(rtex, this);

				// shut it down
				BeginShutdown();
			}
		}

		/// <summary>
		/// audio flow state changed
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void AudioFlowStateChanged(object sender, Microsoft.Rtc.Collaboration.MediaFlowStateChangedEventArgs e)
		{
			// flow
			Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoFlow flow = sender as Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoFlow;

			// unregister                
			flow.StateChanged -= AudioFlowStateChanged;

			// check active
			if (e.State == Microsoft.Rtc.Collaboration.MediaFlowState.Active)
			{
				// TODO: log state
			}
			else
			{
				// shut down
				BeginShutdown();

				// log
				EFS.EventLog.Log.LogInfo("OfficeService: terminated unexpectedly",this);
			}
		}

		/// <summary>
		/// Configuration request for audio flow
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
		private void AudioVideoFlowConfigurationRequested(object sender, Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoFlowConfigurationRequestedEventArgs e)
		{
			// register for state changes
			e.Flow.StateChanged += AudioFlowStateChanged;

			// init template
			Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoFlowTemplate template = new Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoFlowTemplate(e.Flow);
			e.Flow.Initialize(template);
		}

		/// <summary>
		/// Start-up complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnStartupComplete(EFS.RC.Common.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnStartupComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("OnStartupComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion

		#region Private Audio-Video Channel


		/// <summary>
		/// Handle attendance changes
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
        private void audioVideoMcuSessionAttendanceChanged(object sender, Microsoft.Rtc.Collaboration.ParticipantEndpointAttendanceChangedEventArgs<Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuParticipantEndpointProperties> args)
        {
			//  end points list
            var endpoints = new System.Collections.ObjectModel.Collection<System.Collections.Generic.KeyValuePair<Microsoft.Rtc.Collaboration.ParticipantEndpoint, 
				Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuParticipantEndpointProperties>>();
        }

		/// <summary>
		/// Handle participants properties change
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="e"></param>
        private void AudioVideoMcuSession_ParticipantEndpointPropertiesChanged(object sender, Microsoft.Rtc.Collaboration.ParticipantEndpointPropertiesChangedEventArgs<Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuParticipantEndpointProperties> e)
        {
			//  end points list
            var endpoints = new System.Collections.ObjectModel.Collection<System.Collections.Generic.KeyValuePair<Microsoft.Rtc.Collaboration.ParticipantEndpoint, 
				Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuParticipantEndpointProperties>>();
        }

		/// <summary>
		/// Establish audio channel
		/// </summary>
		internal void EstablishPrivateAudioChannel()
        {
			// register to events
            officeServiceCustomerConference.Conversation.ConferenceSession.AudioVideoMcuSession.ParticipantEndpointAttendanceChanged += this.audioVideoMcuSessionAttendanceChanged;
            officeServiceCustomerConference.Conversation.ConferenceSession.AudioVideoMcuSession.ParticipantEndpointPropertiesChanged += this.AudioVideoMcuSession_ParticipantEndpointPropertiesChanged;

			// get participants
            System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint> listOfParticipantEndpoints = 
				new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint>(officeServiceCustomerConference.Conversation.ConferenceSession.AudioVideoMcuSession.GetRemoteParticipantEndpoints());

			// customer end point
			Microsoft.Rtc.Collaboration.ParticipantEndpoint customerEndpoint = null;

			// find customer end point
            foreach(Microsoft.Rtc.Collaboration.ParticipantEndpoint participantEndpoint in listOfParticipantEndpoints)
            {
				if (EFS.RC.Common.Utilities.SipUriCompare(participantEndpoint.Participant.Uri, officeServiceCustomerConference.CustomerSession.CustomerCall.RemoteEndpoint.Participant.Uri))
                {
                    customerEndpoint = participantEndpoint;
                }
            }

			// check
            if (null == customerEndpoint)
			{
				return;
			}

			// remove registration
            officeServiceCustomerConference.Conversation.ConferenceSession.AudioVideoMcuSession.ParticipantEndpointAttendanceChanged -= this.audioVideoMcuSessionAttendanceChanged;
            officeServiceCustomerConference.Conversation.ConferenceSession.AudioVideoMcuSession.ParticipantEndpointPropertiesChanged -= this.AudioVideoMcuSession_ParticipantEndpointPropertiesChanged;

			// make routes
            Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute outgoingRoute = new Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute(customerEndpoint);
            outgoingRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;
            Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute incomingRoute = new Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute(customerEndpoint);
            incomingRoute.IsDtmfEnabled = true;
            incomingRoute.Operation = Microsoft.Rtc.Collaboration.AudioVideo.RouteUpdateOperation.Add;

			// update
            try
            {
                serviceCall.AudioVideoMcuRouting.BeginUpdateAudioRoutes(
					new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.OutgoingAudioRoute>() { outgoingRoute },
                    new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.AudioVideo.IncomingAudioRoute>() { incomingRoute },
					EndUpdateAudioRoutes,
					null);
            }
            catch (System.InvalidOperationException ivoex)
            {
				EFS.EventLog.Log.LogException(ivoex, this);

				// shut it down
				BeginShutdown();
            }
        }

		/// <summary>
		/// End establish
		/// </summary>
		/// <param name="result"></param>
		public void EndUpdateAudioRoutes(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndEstablisContextChannel", this);

			try
			{
				// try 
				serviceCall.AudioVideoMcuRouting.EndUpdateAudioRoutes(ar);
			}
			catch (Microsoft.Rtc.Signaling.RealTimeException rtex)
			{
				EFS.EventLog.Log.LogException(rtex, this);

				// shut it down
				BeginShutdown();
			}
		}

		#endregion

		internal void BringAllChannelParticipantsInMainAudioMix()
		{
			lock (syncAccess)
			{
				// get participants
				System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint> listOfParticipantEndpoints = 
					new System.Collections.Generic.List<Microsoft.Rtc.Collaboration.ParticipantEndpoint>(officeServiceCustomerConference.Conversation.ConferenceSession.AudioVideoMcuSession.GetRemoteParticipantEndpoints());

				// find customer end point
				foreach (Microsoft.Rtc.Collaboration.ParticipantEndpoint participantEndpoint in listOfParticipantEndpoints)
				{
					Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuSession audioVideoMcuSession = officeServiceCustomerConference.Conversation.ConferenceSession.AudioVideoMcuSession;

					try
					{
						audioVideoMcuSession.BeginAddToDefaultRouting(participantEndpoint, EndAddToDefaultRouting, null);
					}
					catch (System.InvalidOperationException ivoex)
					{
					}
				}
            }	
		}

		/// <summary>
		/// End establish
		/// </summary>
		/// <param name="result"></param>
		public void EndAddToDefaultRouting(System.IAsyncResult ar)
		{
			EFS.EventLog.Log.LogInfo("EndEstablisContextChannel", this);

			try
			{
				// try 
				Microsoft.Rtc.Collaboration.AudioVideo.AudioVideoMcuSession audioVideoMcuSession = officeServiceCustomerConference.Conversation.ConferenceSession.AudioVideoMcuSession;

				audioVideoMcuSession.EndAddToDefaultRouting(ar);
			}
			catch (Microsoft.Rtc.Signaling.RealTimeException rtex)
			{
				EFS.EventLog.Log.LogException(rtex, this);

				// shut it down
				BeginShutdown();
			}
		}

		#region Runtime

		/// <summary>
		/// Call state changed
		/// </summary>
		/// <param name="sender"></param>
		/// <param name="args"></param>
		private void CallStateChanged(object sender, Microsoft.Rtc.Collaboration.CallStateChangedEventArgs args)
		{
			if (args.State == Microsoft.Rtc.Collaboration.CallState.Terminating)
			{
				// terminate
				//TODO: handle end terminate 
				serviceCall.BeginTerminate(null, null);

				// TODO terminate channel
			}
			else if (args.State == Microsoft.Rtc.Collaboration.CallState.Terminated)
			{
				// check flow and detach any used features
				if (null != serviceCall.Flow)
				{
					if (null != serviceCall.Flow.Player)
					{
						serviceCall.Flow.Player.DetachFlow(serviceCall.Flow);
					}

					if (null != serviceCall.Flow.Recorder)
					{
						serviceCall.Flow.Recorder.DetachFlow();
					}

					if (null != serviceCall.Flow.ToneController)
					{
						serviceCall.Flow.ToneController.DetachFlow();
					}
				}
			}
		}


		#endregion

		#region Shutdown

		/// <summary>
		/// Shut down
		/// </summary>
		public void Shutdown()
		{
			EFS.EventLog.Log.LogInfo("Shutdown", this);

			// terninate context channel
			officeServiceCustomerConference.BeginShutdown();
		}

				/// <summary>
		/// Shut down complete
		/// </summary>
		/// <param name="ar"></param>
		private void OnShutdownComplete(EFS.RC.Common.AsyncProcess ap)
		{
			// log
			EFS.EventLog.Log.LogInfo("OnShutdownComplete", this);

			try
			{
				// check
				ap.Throw();

				// log
				EFS.EventLog.Log.LogInfo("OnShutdownComplete SUCCCESS", this);
			}
			catch (System.Exception ex)
			{
				// log
				EFS.EventLog.Log.LogException(ex, this);
			}
		}

		#endregion
	}
}
